package s.f.contribuyentes;

import s.f.lang.Cadena;
import s.f.lang.Caracter;
import s.f.util.AppException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Realiza validaciones para el RFC (Fisica/Moral) y CURP.
 *
 * @author Froy
 */
public class Validaciones {

    private static final String REGEXPJM = "([J][O][S][E]|[M][A][R][I][A]|[M][A]|[M][A][.]|[J]|[J][.])";
    private static final String REGEXPDIGV = "[^A-ZÑ 0-9&]";

    /**
     * Verificar si el nombre es compuesto, se le llama nombre compuesto al
     * haber 2 o más nombres en el nombre completo de la persona.
     *
     * @param nombreCompleto
     * @return
     */
    public static boolean isNombreCompuesto(String nombreCompleto) {
        return (nombreCompleto.contains(" ")) ? true : false;
    }

    /**
     * Verifica si la cadena contiene puntos que los separen, de forma que
     * asimile que son iniciales o siglas.
     *
     * @param palabra
     * @return
     */
    public static boolean hasIniciales(String palabra) {
        return (palabra.contains(".")) ? true : false;
    }

    /**
     * Verifica si contiene otro nombre aparte de JOSE o MARIA si fuera el caso
     * y el nombre completo contenga otro diferente a JOSE o MARIA ver la
     * expresion regular REGEXPJM entonces estos se eliminan.
     *
     * @param nombreCompleto
     * @return
     * @see #REGEXPJM
     */
    public static String getNameDiffJMsiContiene(String nombreCompleto) {
        String nombre = String.valueOf(nombreCompleto.replaceAll(REGEXPJM, "").trim());
        return (nombre.length() > 0) ? nombre : nombreCompleto;
    }

    /**
     * <p>Obtiene la Homonimia del RFC <a
     * href="http://www.iec.uia.mx/acad/nathan/arqweb/practica2.pdf">Info
     * Obtener Homonimia</a> con la regla del & = Ñ <a
     * href="https://www.recaudanet.gob.mx/recaudanet/rfc.jsp">Obtner RFC
     * Online</a> 1969 Ñ=40 / 1977 Ñ=10</p>
     *
     * @param nombreCompleto
     * @return
     */
    public static String getHomonimiaRFC(String nombreCompleto, int tipo) {
        Map mapHominomo = new HashMap();

        mapHominomo.put(' ', "00"); //Espacio
        mapHominomo.put('0', "00");
        mapHominomo.put('1', "01");
        mapHominomo.put('2', "02");
        mapHominomo.put('3', "03");
        mapHominomo.put('4', "04");
        mapHominomo.put('5', "05");
        mapHominomo.put('6', "06");
        mapHominomo.put('7', "07");
        mapHominomo.put('8', "08");
        mapHominomo.put('9', "09");
        mapHominomo.put('&', "10");
        if (tipo <= 1974) { //Verificar... 1969 Ñ=40 / 1977 Ñ=10 
            mapHominomo.put('Ñ', "40");
        } else {
            mapHominomo.put('Ñ', "10");
        }
        mapHominomo.put('A', "11");
        mapHominomo.put('B', "12");
        mapHominomo.put('C', "13");
        mapHominomo.put('D', "14");
        mapHominomo.put('E', "15");
        mapHominomo.put('F', "16");
        mapHominomo.put('G', "17");
        mapHominomo.put('H', "18");
        mapHominomo.put('I', "19");
        //No existe el 20 en la table del anexo I Homonimia
        mapHominomo.put('J', "21");
        mapHominomo.put('K', "22");
        mapHominomo.put('L', "23");
        mapHominomo.put('M', "24");
        mapHominomo.put('N', "25");
        mapHominomo.put('O', "26");
        mapHominomo.put('P', "27");
        mapHominomo.put('Q', "28");
        mapHominomo.put('R', "29");
        //No existe el 30 en la table del anexo I Homonimia
        //No existe el 31 en la table del anexo I Homonimia
        mapHominomo.put('S', "32");
        mapHominomo.put('T', "33");
        mapHominomo.put('U', "34");
        mapHominomo.put('V', "35");
        mapHominomo.put('W', "36");
        mapHominomo.put('X', "37");
        mapHominomo.put('Y', "38");
        mapHominomo.put('Z', "39");

        //----------------------------------------
        Map mapHominomoCocienteResiduo = new HashMap();
        mapHominomoCocienteResiduo.put(0, "1");
        mapHominomoCocienteResiduo.put(1, "2");
        mapHominomoCocienteResiduo.put(2, "3");
        mapHominomoCocienteResiduo.put(3, "4");
        mapHominomoCocienteResiduo.put(4, "5");
        mapHominomoCocienteResiduo.put(5, "6");
        mapHominomoCocienteResiduo.put(6, "7");
        mapHominomoCocienteResiduo.put(7, "8");
        mapHominomoCocienteResiduo.put(8, "9");
        mapHominomoCocienteResiduo.put(9, "A");
        mapHominomoCocienteResiduo.put(10, "B");
        mapHominomoCocienteResiduo.put(11, "C");
        mapHominomoCocienteResiduo.put(12, "D");
        mapHominomoCocienteResiduo.put(13, "E");
        mapHominomoCocienteResiduo.put(14, "F");
        mapHominomoCocienteResiduo.put(15, "G");
        mapHominomoCocienteResiduo.put(16, "H");
        mapHominomoCocienteResiduo.put(17, "I");
        mapHominomoCocienteResiduo.put(18, "J");
        mapHominomoCocienteResiduo.put(19, "K");
        mapHominomoCocienteResiduo.put(20, "L");
        mapHominomoCocienteResiduo.put(21, "M");
        mapHominomoCocienteResiduo.put(22, "N");
        mapHominomoCocienteResiduo.put(23, "P");
        mapHominomoCocienteResiduo.put(24, "Q");
        mapHominomoCocienteResiduo.put(25, "R");
        mapHominomoCocienteResiduo.put(26, "S");
        mapHominomoCocienteResiduo.put(27, "T");
        mapHominomoCocienteResiduo.put(28, "U");
        mapHominomoCocienteResiduo.put(29, "V");
        mapHominomoCocienteResiduo.put(30, "W");
        mapHominomoCocienteResiduo.put(31, "X");
        mapHominomoCocienteResiduo.put(32, "Y");
        mapHominomoCocienteResiduo.put(33, "Z");

        String num = "0";
        int sum = 0;

        for (char n : nombreCompleto.toCharArray()) {
            num += String.valueOf(mapHominomo.get(n));
        }


        //j++ j+=2
        for (int i = 0, j = 2, cont = 1; j <= num.length(); i++, j++, cont++) {
            int a = Integer.parseInt(num.substring(i, j));
            int b = Integer.parseInt(String.valueOf(num.charAt(cont)));
            //OK
            sum += a * b;
        }

        ArrayList list = new ArrayList();
        if (sum >= 100) {
            String sumS = String.valueOf(sum);
            sumS = sumS.substring(sumS.length() - 3, sumS.length());
            sum = Integer.parseInt(sumS);

            int cocinete = sum / 34;
            int residuo = sum % 34;

            list.add(mapHominomoCocienteResiduo.get(cocinete));
            list.add(mapHominomoCocienteResiduo.get(residuo));
        }
        return list.toString();
    }

    /**
     * Obtner Dígito Verificador del RFC.
     *
     * @param RFCsinDigV
     * @return
     * @see #REGEXPDIGV
     */
    public static String getDigitoVerificadorRFC(String RFCsinDigV) {
        Map mapDigitoVerificador = new HashMap();
        mapDigitoVerificador.put('0', "00");
        mapDigitoVerificador.put('1', "01");
        mapDigitoVerificador.put('2', "02");
        mapDigitoVerificador.put('3', "03");
        mapDigitoVerificador.put('4', "04");
        mapDigitoVerificador.put('5', "05");
        mapDigitoVerificador.put('6', "06");
        mapDigitoVerificador.put('7', "07");
        mapDigitoVerificador.put('8', "08");
        mapDigitoVerificador.put('9', "09");
        mapDigitoVerificador.put('A', "10");
        mapDigitoVerificador.put('B', "11");
        mapDigitoVerificador.put('C', "12");
        mapDigitoVerificador.put('D', "13");
        mapDigitoVerificador.put('E', "14");
        mapDigitoVerificador.put('F', "15");
        mapDigitoVerificador.put('G', "16");
        mapDigitoVerificador.put('H', "17");
        mapDigitoVerificador.put('I', "18");
        mapDigitoVerificador.put('J', "19");
        mapDigitoVerificador.put('K', "20");
        mapDigitoVerificador.put('L', "21");
        mapDigitoVerificador.put('M', "22");
        mapDigitoVerificador.put('N', "23");
        mapDigitoVerificador.put('Ñ', "24");
        mapDigitoVerificador.put('O', "25");
        mapDigitoVerificador.put('P', "26");
        mapDigitoVerificador.put('Q', "27");
        mapDigitoVerificador.put('R', "28");
        mapDigitoVerificador.put('S', "29");
        mapDigitoVerificador.put('T', "30");
        mapDigitoVerificador.put('U', "31");
        mapDigitoVerificador.put('V', "32");
        mapDigitoVerificador.put('W', "33");
        mapDigitoVerificador.put('X', "34");
        mapDigitoVerificador.put('Y', "35");
        mapDigitoVerificador.put('Z', "36");
        mapDigitoVerificador.put(' ', "37");
        mapDigitoVerificador.put('&', "38");
        mapDigitoVerificador.put(REGEXPDIGV, "00");

        ArrayList list = new ArrayList();
        int i = (RFCsinDigV.toCharArray().length + 1);
        int sum = 0;
        for (char n : RFCsinDigV.toCharArray()) {
            int a = (Integer.parseInt(String.valueOf(mapDigitoVerificador.get(n))) * i--);
            sum += a;
        }
        int cociente = sum / 11;
        int residuo = sum % 11;

        String dv = "0";

        if (residuo == 0) {
            dv = "0";
        } else if (residuo > 0) {
            dv = (11 - residuo == 10) ? "A" : String.valueOf(11 - residuo);
        }
        //list.add(mapDigitoVerificador.get(n));
        return dv;
    }

    /**
     * Obtener el Dígito verficador.
     *
     * @param curpSinDV
     * @return
     */
    public static int getCURPDigitoVerificador(String curpSinDV) {
        Map mapaDigitoVerificador = new HashMap();
        mapaDigitoVerificador.put('0', 0);
        mapaDigitoVerificador.put('1', 1);
        mapaDigitoVerificador.put('2', 2);
        mapaDigitoVerificador.put('3', 3);
        mapaDigitoVerificador.put('4', 4);
        mapaDigitoVerificador.put('5', 5);
        mapaDigitoVerificador.put('6', 6);
        mapaDigitoVerificador.put('7', 7);
        mapaDigitoVerificador.put('8', 8);
        mapaDigitoVerificador.put('9', 9);
        mapaDigitoVerificador.put('A', 10);
        mapaDigitoVerificador.put('B', 11);
        mapaDigitoVerificador.put('C', 12);
        mapaDigitoVerificador.put('D', 13);
        mapaDigitoVerificador.put('E', 14);
        mapaDigitoVerificador.put('F', 15);
        mapaDigitoVerificador.put('G', 16);
        mapaDigitoVerificador.put('H', 17);
        mapaDigitoVerificador.put('I', 18);
        mapaDigitoVerificador.put('J', 19);
        mapaDigitoVerificador.put('K', 20);
        mapaDigitoVerificador.put('L', 21);
        mapaDigitoVerificador.put('M', 22);
        mapaDigitoVerificador.put('N', 23);
        mapaDigitoVerificador.put('Ñ', 24);
        mapaDigitoVerificador.put('O', 25);
        mapaDigitoVerificador.put('P', 26);
        mapaDigitoVerificador.put('Q', 27);
        mapaDigitoVerificador.put('R', 28);
        mapaDigitoVerificador.put('S', 29);
        mapaDigitoVerificador.put('T', 30);
        mapaDigitoVerificador.put('U', 31);
        mapaDigitoVerificador.put('V', 32);
        mapaDigitoVerificador.put('W', 33);
        mapaDigitoVerificador.put('X', 34);
        mapaDigitoVerificador.put('Y', 35);
        mapaDigitoVerificador.put('Z', 36);

        int contador = 18;
        int sum = 0;

        for (char m : curpSinDV.toCharArray()) {
            sum += Integer.parseInt(String.valueOf(mapaDigitoVerificador.get(m))) * contador--;
        }

        sum = 10 - sum % 10;
        return (sum == 10) ? 0 : sum;
    }

    /**
     * Obtener el Homoclave
     *
     * @param nombreCompleto
     * @param tipo
     * @return
     */
    public static String getHomoclaveMoral(String nombreCompleto, int tipo) {
        Map mapHominomo = new HashMap();
        mapHominomo.put(' ', "00"); //Espacio
        mapHominomo.put('0', "00");
        mapHominomo.put('1', "01");
        mapHominomo.put('2', "02");
        mapHominomo.put('3', "03");
        mapHominomo.put('4', "04");
        mapHominomo.put('5', "05");
        mapHominomo.put('6', "06");
        mapHominomo.put('7', "07");
        mapHominomo.put('8', "08");
        mapHominomo.put('9', "09");
        mapHominomo.put('&', "10");
        if (tipo <= 1974) { //Verificar... 1969 Ñ=40 / 1977 Ñ=10 
            mapHominomo.put('Ñ', "40");
        } else {
            mapHominomo.put('Ñ', "10");
        }
        mapHominomo.put('A', "11");
        mapHominomo.put('B', "12");
        mapHominomo.put('C', "13");
        mapHominomo.put('D', "14");
        mapHominomo.put('E', "15");
        mapHominomo.put('F', "16");
        mapHominomo.put('G', "17");
        mapHominomo.put('H', "18");
        mapHominomo.put('I', "19");
        //No existe el 20 en la table del anexo I Homonimia
        mapHominomo.put('J', "21");
        mapHominomo.put('K', "22");
        mapHominomo.put('L', "23");
        mapHominomo.put('M', "24");
        mapHominomo.put('N', "25");
        mapHominomo.put('O', "26");
        mapHominomo.put('P', "27");
        mapHominomo.put('Q', "28");
        mapHominomo.put('R', "29");
        //No existe el 30 en la table del anexo I Homonimia
        //No existe el 31 en la table del anexo I Homonimia
        mapHominomo.put('S', "32");
        mapHominomo.put('T', "33");
        mapHominomo.put('U', "34");
        mapHominomo.put('V', "35");
        mapHominomo.put('W', "36");
        mapHominomo.put('X', "37");
        mapHominomo.put('Y', "38");
        mapHominomo.put('Z', "39");


        //----------------------------------------
        Map mapHominomoCocienteResiduo = new HashMap();
        mapHominomoCocienteResiduo.put(0, "1");
        mapHominomoCocienteResiduo.put(1, "2");
        mapHominomoCocienteResiduo.put(2, "3");
        mapHominomoCocienteResiduo.put(3, "4");
        mapHominomoCocienteResiduo.put(4, "5");
        mapHominomoCocienteResiduo.put(5, "6");
        mapHominomoCocienteResiduo.put(6, "7");
        mapHominomoCocienteResiduo.put(7, "8");
        mapHominomoCocienteResiduo.put(8, "9");
        mapHominomoCocienteResiduo.put(9, "A");
        mapHominomoCocienteResiduo.put(10, "B");
        mapHominomoCocienteResiduo.put(11, "C");
        mapHominomoCocienteResiduo.put(12, "D");
        mapHominomoCocienteResiduo.put(13, "E");
        mapHominomoCocienteResiduo.put(14, "F");
        mapHominomoCocienteResiduo.put(15, "G");
        mapHominomoCocienteResiduo.put(16, "H");
        mapHominomoCocienteResiduo.put(17, "I");
        mapHominomoCocienteResiduo.put(18, "J");
        mapHominomoCocienteResiduo.put(19, "K");
        mapHominomoCocienteResiduo.put(20, "L");
        mapHominomoCocienteResiduo.put(21, "M");
        mapHominomoCocienteResiduo.put(22, "N");
        mapHominomoCocienteResiduo.put(23, "P");
        mapHominomoCocienteResiduo.put(24, "Q");
        mapHominomoCocienteResiduo.put(25, "R");
        mapHominomoCocienteResiduo.put(26, "S");
        mapHominomoCocienteResiduo.put(27, "T");
        mapHominomoCocienteResiduo.put(28, "U");
        mapHominomoCocienteResiduo.put(29, "V");
        mapHominomoCocienteResiduo.put(30, "W");
        mapHominomoCocienteResiduo.put(31, "X");
        mapHominomoCocienteResiduo.put(32, "Y");
        mapHominomoCocienteResiduo.put(33, "Z");

        String num = "0";
        int sum = 0;

        for (char n : nombreCompleto.toCharArray()) {
            num += String.valueOf(mapHominomo.get(n));
        }


        //j++ j+=2
        for (int i = 0, j = 2, cont = 1; j <= num.length(); i++, j++, cont++) {
            int a = Integer.parseInt(num.substring(i, j));
            int b = Integer.parseInt(String.valueOf(num.charAt(cont)));
            sum += a * b;
        }

        String sumS = String.valueOf(sum);

        sumS = sumS.substring(sumS.length() - 3, sumS.length());
        sum = Integer.parseInt(sumS);

        int cocinete = sum / 34;
        int residuo = sum % 34;

        ArrayList list = new ArrayList();

        list.add(mapHominomoCocienteResiduo.get(cocinete));
        list.add(mapHominomoCocienteResiduo.get(residuo));

        return list.toString();
    }

    /**
     * Dígito verificador.
     *
     * @param RFCsinDigV
     * @return
     */
    public static String getDigitoVerificadorMoral(String RFCsinDigV) {
        Map mapDigitoVerificador = new HashMap();
        mapDigitoVerificador.put('0', "00");
        mapDigitoVerificador.put('1', "01");
        mapDigitoVerificador.put('2', "02");
        mapDigitoVerificador.put('3', "03");
        mapDigitoVerificador.put('4', "04");
        mapDigitoVerificador.put('5', "05");
        mapDigitoVerificador.put('6', "06");
        mapDigitoVerificador.put('7', "07");
        mapDigitoVerificador.put('8', "08");
        mapDigitoVerificador.put('9', "09");
        mapDigitoVerificador.put('A', "10");
        mapDigitoVerificador.put('B', "11");
        mapDigitoVerificador.put('C', "12");
        mapDigitoVerificador.put('D', "13");
        mapDigitoVerificador.put('E', "14");
        mapDigitoVerificador.put('F', "15");
        mapDigitoVerificador.put('G', "16");
        mapDigitoVerificador.put('H', "17");
        mapDigitoVerificador.put('I', "18");
        mapDigitoVerificador.put('J', "19");
        mapDigitoVerificador.put('K', "20");
        mapDigitoVerificador.put('L', "21");
        mapDigitoVerificador.put('M', "22");
        mapDigitoVerificador.put('N', "23");
        mapDigitoVerificador.put('Ñ', "24");
        mapDigitoVerificador.put('O', "25");
        mapDigitoVerificador.put('P', "26");
        mapDigitoVerificador.put('Q', "27");
        mapDigitoVerificador.put('R', "28");
        mapDigitoVerificador.put('S', "29");
        mapDigitoVerificador.put('T', "30");
        mapDigitoVerificador.put('U', "31");
        mapDigitoVerificador.put('V', "32");
        mapDigitoVerificador.put('W', "33");
        mapDigitoVerificador.put('X', "34");
        mapDigitoVerificador.put('Y', "35");
        mapDigitoVerificador.put('Z', "36");
        mapDigitoVerificador.put('*', "37");
        mapDigitoVerificador.put(REGEXPDIGV, "00");

        ArrayList list = new ArrayList();
        int i = (RFCsinDigV.toCharArray().length + 1);
        int sum = 0;
        for (char n : RFCsinDigV.toCharArray()) {
            int a = (Integer.parseInt(String.valueOf(mapDigitoVerificador.get(n))) * i--);
            sum += a;
        }

        int cociente = sum / 11;
        int residuo = sum % 11;

        String dv = "0";

        if (residuo == 0) {
            dv = "0";
        } else if (residuo > 0) {
            dv = (11 - residuo == 10) ? "A" : String.valueOf(11 - residuo);
        }

        //list.add(mapDigitoVerificador.get(n));

        return dv;

    }

    /**
     * Obteniendo la clave del Estado.
     *
     * @param RFCsinDigV
     * @return
     */
    public static String getClaveEstado(int RFCsinDigV) {
        Map mapDigitoVerificador = new HashMap();
        mapDigitoVerificador.put(1, "AS");
        mapDigitoVerificador.put(2, "BC");
        mapDigitoVerificador.put(3, "BS");
        mapDigitoVerificador.put(4, "CC");
        mapDigitoVerificador.put(5, "CL");
        mapDigitoVerificador.put(6, "CM");
        mapDigitoVerificador.put(7, "CS");
        mapDigitoVerificador.put(8, "CH");
        mapDigitoVerificador.put(9, "DF");
        mapDigitoVerificador.put(10, "DG");
        mapDigitoVerificador.put(11, "GT");
        mapDigitoVerificador.put(12, "GR");
        mapDigitoVerificador.put(13, "HG");
        mapDigitoVerificador.put(14, "JC");
        mapDigitoVerificador.put(15, "MC");
        mapDigitoVerificador.put(16, "MN");
        mapDigitoVerificador.put(17, "MS");
        mapDigitoVerificador.put(18, "NT");
        mapDigitoVerificador.put(19, "NL");
        mapDigitoVerificador.put(20, "OC");
        mapDigitoVerificador.put(21, "PL");
        mapDigitoVerificador.put(22, "QT");
        mapDigitoVerificador.put(23, "QR");
        mapDigitoVerificador.put(24, "SP");
        mapDigitoVerificador.put(25, "SL");
        mapDigitoVerificador.put(26, "SR");
        mapDigitoVerificador.put(27, "TC");
        mapDigitoVerificador.put(28, "TS");
        mapDigitoVerificador.put(29, "TL");
        mapDigitoVerificador.put(30, "VZ");
        mapDigitoVerificador.put(31, "YN");
        mapDigitoVerificador.put(32, "ZS");
        mapDigitoVerificador.put(0, "NE"); //Nacido en el extranjero

        return String.valueOf(mapDigitoVerificador.get(RFCsinDigV));

    }

    /**
     * Si se cuenta con todos los campos para calcular el RFC normal.
     *
     * @param fnacimeinto
     * @param nombre
     * @param appaterno
     * @param apmaterno
     * @return
     */
    public static String filtroCURP(String fnacimeinto,
            String nombre, String appaterno, String apmaterno, String sexo, int estado) {

        ArrayList lista = new ArrayList();

        try {
            // 1) La primera letra del apellido paterno.
            lista.add(appaterno.charAt(0));

            char vocalValorApPaterno = 'X';
            // 2) La primera vocal del apellido paterno o X en el caso de que no exista vocal.
            // Ejem. ANDRES ICH RODRIGUEZ
            for (char i : appaterno.substring(1).toCharArray()) {
                if (Caracter.isVocal(i)) {
                    vocalValorApPaterno = i;
                    break;
                }
            }
            lista.add(vocalValorApPaterno);

            //3) Si no existe el apellido materno colocar "X".
            // Ejem. LUIS PEREZ PEXL
            lista.add((!apmaterno.isEmpty()) ? apmaterno.charAt(0) : 'X');

            // 4) La primera letra del nombre.
            lista.add(nombre.charAt(0));


            // Añadir Fecha
            //String fecha = Validacion.invertirFecha(fnacimeinto);

            lista.add(fnacimeinto.substring(8));
            lista.add(fnacimeinto.substring(3, 5));
            lista.add(fnacimeinto.substring(0, 2));

            lista.add(sexo);
            //Estado
            lista.add(Validaciones.getClaveEstado(estado));


            char constanteValorApPaterno = 'X';
            // 14) La primera consonante del apellido paterno.
            //Cuando la primera constante interna es la letra "Ñ"
            //el sistema asignará una "X".
            // Ejem. ALBERTO OÑATE RODRIGUEZ
            for (char i : appaterno.substring(1).toCharArray()) {
                if (Caracter.isCostante(i)) {
                    constanteValorApPaterno = (i == 'Ñ' ? 'X' : i);
                    break;
                }
            }
            lista.add(constanteValorApPaterno);

            if (!apmaterno.isEmpty()) {
                char constanteValorApMaterno = 'X';
                // 15) La primera consonante del apellido paterno.
                //Cuando la primera constante interna es la letra "Ñ"
                //el sistema asignará una "X".
                // Ejem. ALBERTO OÑATE RODRIGUEZ
                for (char i : apmaterno.substring(1).toCharArray()) {
                    if (Caracter.isCostante(i)) {
                        constanteValorApMaterno = (i == 'Ñ' ? 'X' : i);
                        break;
                    }
                }
                lista.add(constanteValorApMaterno);
            } else {
                lista.add('X');
            }


            char constanteValorNombre = 'X';
            // 16) La primera consonante del apellido paterno.
            //Cuando la primera constante interna es la letra "Ñ"
            //el sistema asignará una "X".
            // Ejem. ALBERTO OÑATE RODRIGUEZ
            for (char i : nombre.substring(1).toCharArray()) {
                if (Caracter.isCostante(i)) {
                    constanteValorNombre = (i == 'Ñ' ? 'X' : i);
                    break;
                }
            }
            lista.add(constanteValorNombre);


            /*
             * Homonimia 0-9 hasta el año 1999 y A-Z para fechas de nacimiento a
             * partir del 2000.
             * http://www.gobernacion.gob.mx/work/models/SEGOB/Resource/231/1/images/InstructivoParaLaCurp_v2008.pdf
             */
            int anio = Integer.parseInt(fnacimeinto.substring(6, 10));

            if (anio < 2000) {
                lista.add("0");
            } else {
                lista.add("A");
            }

            //Cálculo del dígito verificador del RFC.
            int dv2 = Validaciones.getCURPDigitoVerificador(Cadena.cleanList(lista.toString()));

            lista.add(dv2);
        } catch (StringIndexOutOfBoundsException sobe) {
            new AppException("Faltan datos de capturar", sobe);
        }

        return lista.toString();
    }

    /**
     * Si se cuenta con todos los campos para calcular el RFC normal.
     *
     * @param fnacimeinto
     * @param nombre
     * @param appaterno
     * @param apmaterno
     * @return
     */
    public static String forma1TodoslosCamposRFCPFisica(String fnacimeinto,
            String nombre, String appaterno, String apmaterno) {

        ArrayList lista = new ArrayList();

        try {
            // 1) La primera letra del apellido paterno.
            lista.add(appaterno.charAt(0));
            for (char i : appaterno.substring(1).toCharArray()) {
                if (Caracter.isVocal(i)) {
                    // 2) La primera vocal del apellido paterno.
                    lista.add(i);
                    break;
                }
            }
            // 3) La primera letra del apellido materno.
            lista.add(apmaterno.charAt(0));
            // 4) La primera letra del nombre.
            lista.add(nombre.charAt(0));

            lista.add(fnacimeinto.substring(8));
            lista.add(fnacimeinto.substring(3, 5));
            lista.add(fnacimeinto.substring(0, 2));
        } catch (StringIndexOutOfBoundsException sobe) {
            new AppException("Faltan datos de capturar", sobe);
        }

        return lista.toString();
    }

    /**
     * Si apellido patenro consta de una o dos letras.
     *
     * @param fnacimeinto
     * @param nombre
     * @param appaterno
     * @param apmaterno
     * @return
     */
    public static String forma2SiApellidoPaternoMenor2LetrasRFCPFisica(String fnacimeinto, String nombre,
            String appaterno, String apmaterno) {

        ArrayList lista = new ArrayList();

        try {

            // 1) La primera letra del apellido paterno.
            lista.add(appaterno.charAt(0));
            // 2) La primera letra del apellido materno.
            lista.add(apmaterno.charAt(0));
            // 4) La primera letra del nombre.
            lista.add(nombre.charAt(0));
            // 4) La primera letra del nombre.
            lista.add(nombre.charAt(1));

            lista.add(fnacimeinto.substring(8));
            lista.add(fnacimeinto.substring(3, 5));
            lista.add(fnacimeinto.substring(0, 2));


        } catch (StringIndexOutOfBoundsException e) {
            new AppException("Faltan datos de capturar", e);
        }

        return lista.toString();
    }

    /**
     * Si uno de los apellidos esta vacio.
     *
     * @param fnacimeinto
     * @param nombre
     * @param appaterno
     * @param apmaterno
     * @return
     */
    public static String forma3SiApellidosVaciosRFCPFisica(String fnacimeinto, String nombre, String appaterno, String apmaterno) {
        ArrayList lista = new ArrayList();

        try {
            if (!appaterno.isEmpty()) {
                // 1) La primera letra del apellido paterno.
                lista.add(appaterno.charAt(0));
                // 2) La segunda letra del apellido paterno.
                lista.add(appaterno.charAt(1));
            } else if (!apmaterno.isEmpty()) {
                // 1) La primera letra del apellido materno.
                lista.add(apmaterno.charAt(0));
                // 2) La segunda letra del apellido materno.
                lista.add(apmaterno.charAt(1));
            }

            // 3) La primera letra del nombre.
            lista.add(nombre.charAt(0));
            // 4) La segunda letra del nombre.
            lista.add(nombre.charAt(1));

            lista.add(fnacimeinto.substring(8));
            lista.add(fnacimeinto.substring(3, 5));
            lista.add(fnacimeinto.substring(0, 2));

        } catch (StringIndexOutOfBoundsException e) {
            new AppException("Faltan datos de capturar", e);
        }

        return lista.toString();
    }
}